//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <aura.h>
#include "win32.h"
#include "private.h"
#include "peimage.h"

#define SIZE_OF_NT_SIGNATURE    sizeof(DWORD)
#define OPTHDROFFSET(a) ((Aura_PVoid)((BYTE *)a      +  \
             ((PIMAGE_DOS_HEADER)a)->e_lfanew   +  \
             SIZE_OF_NT_SIGNATURE               +  \
             sizeof (IMAGE_FILE_HEADER)))

AURA_API AuraModule_Load(
        /* [in] */ Aura_PWChar name,
        /* [in] */ Aura_ModuleLoadFlags flags,
        /* [out] */ Aura_Handle * phModule)
{
    HINSTANCE hModule = NULL;

    assert(AURA_NULL != name);
    assert(L'\0' != (wchar_t)name[0]);
    assert(NULL != phModule);
    Sleep(0); //BUGBUG: loadlibray would fail if not add this
    hModule = GetModuleHandleW((LPCWSTR)(const wchar_t *)name);
    if (NULL == hModule) {
        hModule = LoadLibraryW((LPCWSTR)(const wchar_t *)name);
        if (NULL == hModule) {
            goto E_FAIL_EXIT;
        }
    }

    *phModule = hModule;
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraModule_Unload(
        /* [in] */ Aura_Handle hModule)
{
    BOOL bSuccess = FALSE;

    bSuccess = FreeLibrary((HMODULE)hModule);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraModule_Close(
        /* [in] */ Aura_Handle hModule)
{
   BOOL bSuccess = FALSE;

    assert(NULL != hModule);
    bSuccess = CloseHandle((HANDLE)hModule);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraModule_CreateModuleSnapshot(
        /* [in] */ Aura_ProcessId processId,
        /* [out] */ Aura_Handle * phSnapshot)
{
    HANDLE hSnapshot = NULL;

    assert(NULL != phSnapshot);

    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, (DWORD)processId);
    if (INVALID_HANDLE_VALUE == hSnapshot) {
        goto E_FAIL_EXIT;
    }

    *phSnapshot = (Aura_Handle)hSnapshot;

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraModule_CloseModuleSnapshot(
        /* [in] */ Aura_Handle hSnapshot)
{
    BOOL bSuccess = FALSE;

    assert(NULL != hSnapshot);
    // BUG: should call "CloaseTooHelp32Snapshot" to release the snapshot.
    bSuccess = CloseHandle((HANDLE)hSnapshot);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraModule_GetModuleNode(
        /* [in] */ Aura_Handle hSnapshot,
        /* [in] */ Aura_Bool bFirstNode,
        /* [out] */ Aura_Bool * pbExistedMore,
        /* [out] */ Aura_Handle * phModule)
{
    PMODULEENTRY32 pModuleEntry = NULL;
    BOOL bSuccess = FALSE;
    DWORD dwError;

    assert(NULL != hSnapshot);
    assert(NULL != pbExistedMore);
    assert(NULL != phModule);

    pModuleEntry = (PMODULEENTRY32)Aura_malloc(sizeof(MODULEENTRY32));
    if (NULL == pModuleEntry) {
        return AURA_E_OUT_OF_MEMORY;
    }
    memset((void*)pModuleEntry, 0, sizeof(MODULEENTRY32));
    pModuleEntry->dwSize = sizeof(MODULEENTRY32);

    if (bFirstNode) {
        bSuccess = Module32First((HANDLE)hSnapshot, pModuleEntry);
    }
    else {
        bSuccess = Module32Next((HANDLE)hSnapshot, pModuleEntry);
    }
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }

    *pbExistedMore = TRUE;
    *phModule = (Aura_Handle) pModuleEntry->hModule;
    Aura_free(pModuleEntry);
    pModuleEntry = NULL;

    return AURA_NOERROR;

E_FAIL_EXIT:
    *pbExistedMore = FALSE;
    *phModule = NULL;
    Aura_free(pModuleEntry);
    pModuleEntry = NULL;

    dwError = GetLastError();
    if (ERROR_NO_MORE_FILES  == dwError) {
        return AURA_NOERROR;
    }
    return Win32ErrorToECodeError(dwError);
}

AURA_API AuraModule_GetInformation(
        /* [in] */ Aura_ProcessId processId,
        /* [in] */ Aura_Handle hModule,
        /* [out] */ Aura_Address * pBaseAddr,
        /* [out] */ Aura_Address * pEntryAddr,
        /* [out] */ Aura_MemorySize * pSizeOfImage)
{
    HANDLE hProcess = NULL;
    MODULEINFO moduleInfo;
    Aura_Bool bSuccess = FALSE;
    Aura_ECode ec = AURA_NOERROR;

    assert(NULL != hModule);
    assert(NULL != pBaseAddr);
    assert(NULL != pEntryAddr);
    assert(NULL != pSizeOfImage);

    hProcess = OpenProcess(ALL_ACCESS_RIGHT, FALSE, (DWORD)processId);
    if (NULL == hProcess) {
        goto E_FAIL_EXIT;
    }
    memset((void *)&moduleInfo, 0, sizeof(moduleInfo));
    bSuccess = GetModuleInformation(hProcess,
                                   hModule,
                                   &moduleInfo,
                                   sizeof(moduleInfo));
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }
    CloseHandle(hProcess);

    *pBaseAddr  = (Aura_Address)moduleInfo.lpBaseOfDll;
    *pEntryAddr = (Aura_Address)moduleInfo.EntryPoint;
    *pSizeOfImage = (Aura_MemorySize)moduleInfo.SizeOfImage;

    return ec;

E_FAIL_EXIT:
    ec = GetLastECodeError();
    if (NULL != hProcess) {
        CloseHandle(hProcess);
        hProcess = NULL;
    }
    return ec;
}

AURA_API AuraModule_GetFileName(
        /* [in] */ Aura_Handle hModule,
        /* [out] */ wchar_t * pFileName,
        /* [out] */ wchar_t * pPathName)
{
#define __MAX_PATH__ 260
    DWORD   length = 0;
    wchar_t fileName[__MAX_PATH__];
    wchar_t * pLastSlash = NULL;

    assert(NULL != hModule);
    assert(NULL != pFileName);
    assert(NULL != pPathName);

    length = GetModuleFileNameW(hModule, fileName, __MAX_PATH__);
    if (!length) {
        goto E_FAIL_EXIT;
    }
    pLastSlash = wcsrchr(fileName, L'\\');
    wcscpy((wchar_t *)pFileName, pLastSlash + 1);

    wcscpy((wchar_t *)pPathName, fileName);

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}


AURA_API AuraModule_GetProcAddressByName(
        /* [in] */ Aura_Handle hModule,
        /* [in] */ Aura_PChar name,
        /* [out] */ Aura_Address * pProcAddress)
{
    FARPROC procAddr = NULL;

    assert(NULL != hModule);
    assert(AURA_NULL != name);
    assert('\0' != (char)name[0]);
    assert(NULL != pProcAddress);

    procAddr = GetProcAddress((HINSTANCE)hModule, (LPCSTR)(const char*)name);
    if (NULL == procAddr) {
        goto E_FAIL_EXIT;
    }
    *pProcAddress = (Aura_Address)procAddr;

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraModule_GetProcAddressByOrdinal(
        /* [in] */ Aura_Handle hModule,
        /* [in] */ Aura_Int32 ordinal,
        /* [out] */ Aura_Address * pProcAddress)
{
    FARPROC procAddr = NULL;

    assert(NULL != hModule);
    assert(ordinal >= 0);
    assert(NULL != pProcAddress);

    procAddr = GetProcAddress((HINSTANCE)hModule, (LPCSTR)ordinal);
    if (NULL == procAddr) {
        goto E_FAIL_EXIT;
    }
    *pProcAddress = (Aura_Address)procAddr;

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraModule_GetResourceSection(
        /* [in] */ Aura_Address imageBaseAddr,
        /* [out] */ Aura_Address * pSectBaseAddr)
{
    PIMAGE_OPTIONAL_HEADER pOptHeader = NULL;
    DWORD imageDir = IMAGE_DIRECTORY_ENTRY_RESOURCE;
    Aura_Address offset;

    pOptHeader = (PIMAGE_OPTIONAL_HEADER)OPTHDROFFSET(imageBaseAddr);
   // pSecHeader = (PIMAGE_SECTION_HEADER)SectionHeaderOffset(imageBaseAddr);

    if ( pOptHeader->NumberOfRvaAndSizes <= imageDir) {
        *pSectBaseAddr = NULL;
        return AURA_E_DOES_NOT_EXIST;
    }
    offset = (Aura_Address)pOptHeader->DataDirectory[imageDir].VirtualAddress;
    *pSectBaseAddr = imageBaseAddr + offset;

    return AURA_NOERROR;
}

AURA_API AuraModule_GetResource(
    /* [in] */ Aura_Handle hModule,
    /* [in] */ Aura_PWChar resName,
    /* [in] */ Aura_PWChar resType,
    /* [out] */ Aura_Address * pAddress,
    /* [out] */ Aura_MemorySize * pSize)
{
    HRSRC   hResInfo = NULL;
    HGLOBAL hResData = NULL;
    LPVOID  pResData = NULL;
    DWORD resSize;

    assert(AURA_NULL != resName);
    assert(L'\0' != (wchar_t)resName[0]);
    assert(AURA_NULL != resType);
    assert(L'\0' != (wchar_t)resType[0]);
    assert(NULL != pAddress);
    assert(NULL != pSize);

    hResInfo = FindResourceW((HINSTANCE)hModule,
                            (LPCWSTR)(const wchar_t *)resName,
                            (LPCWSTR)(const wchar_t *)resType);
    if (NULL == hResInfo) {
        goto E_FAIL_EXIT;
    }

    resSize = SizeofResource((HINSTANCE)hModule, hResInfo);
    if (0 == resSize) {
        goto E_FAIL_EXIT;
    }
    *pSize = (Aura_MemorySize)resSize;

    hResData = LoadResource((HINSTANCE)hModule, hResInfo);
    if (NULL == hResData) {
        goto E_FAIL_EXIT;
    }
    pResData = LockResource(hResData);
    if (NULL == pResData) {
        goto E_FAIL_EXIT;
    }
    *pAddress = (Aura_Address)pResData;

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

